package com.hontech.desktoppastacooker.utils

import android.os.Environment
import com.hontech.desktoppastacooker.exception.LoginException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.Calendar
import java.util.Date
import java.util.concurrent.TimeUnit

object Http {

    const val BASE_URL = "http://local.hontech-rdcenter.com:23331/api"

    private val JSON = "application/json;charset=utf8".toMediaType()

    private val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .build()

    private var token = ""

    @kotlinx.serialization.Serializable
    data class ErrResp(
        val err_code: Int,
        val err_msg: String
    )

    @kotlinx.serialization.Serializable
    data class LoginReq(
        val account: String,
        val pwd: String
    )

    @kotlinx.serialization.Serializable
    data class User(
        val id: Long,
        val name: String,
        val account: String,
        val pwd_enc: String
    )

    @kotlinx.serialization.Serializable
    data class LoginRes(
        val user: User,
        val token: String
    )

    private fun parseErrResp(resp: Response) {
        val body = resp.body?.string() ?: throw IOException("网络错误")
        val errResp = defJson.decodeFromString<ErrResp>(body)
        throw LoginException(errResp.err_code, errResp.err_msg)
    }

    private fun loginIfNotToken() {
        val login = LoginReq("device", "device1234")
        val reqBody = defJson.encodeToString(login)
        val req = Request.Builder()
            .post(reqBody.toRequestBody(JSON))
            .url("${BASE_URL}/user/login")
            .build()
        val call = client.newCall(req)
        val resp = call.execute()
        if (resp.code != 200) {
            throw IOException("http响应错误:${resp.code}")
        }
        val head = resp.headers["resp"] ?: throw IOException("数据异常")
        if (head != "ok") {
            parseErrResp(resp)
        }
        val body = resp.body?.string() ?: throw IOException("网络错误")
        val res = defJson.decodeFromString<LoginRes>(body)
        token = res.token
        log("登录成功 $res")
    }

    private fun getResp(url: String): Response {
        val req = Request.Builder()
            .get()
            .url("${BASE_URL}${url}")
            .addHeader("token", token)
            .build()
        val call = client.newCall(req)
        val resp = call.execute()
        if (resp.code != 200) {
            throw IOException("http响应错误:${resp.code}")
        }
        val head = resp.headers["resp"] ?: throw IOException("数据异常")
        if (head == "fail") {
            parseErrResp(resp)
        }
        return resp
    }

    private fun doGetResp(url: String): Response {

        val resp = try {
            getResp(url)
        } catch (e: LoginException) {
            e.printStackTrace()
            loginIfNotToken()
            getResp(url)
        }

        return resp
    }

    private fun doGet(url: String): ByteArray {
        val resp = doGetResp(url)
        return resp.body?.bytes() ?: throw IOException("网络错误")
    }

    suspend fun doDownloadApk(id: Int, cb: (Long) -> Unit): String {

        return PromiseTask {
            val url = "/dev_file/get?id=${id}"
            val file = File(Environment.getExternalStorageDirectory(), "client.apk")
            if (!file.exists()) {
                file.createNewFile()
            }
            val out = FileOutputStream(file)
            val body = doGetResp(url).body ?: throw IOException("网络错误")
            val size = body.contentLength()
            val input = body.byteStream()
            input.use {
                out.use {
                    val buf = ByteArray(1024 * 10)
                    var index = 0L
                    while (index < size) {

                        val n = input.read(buf)
                        if (n <= 0) {
                            throw IOException("recv fail")
                        }
                        out.write(buf, 0, n)
                        index += n
                        AppTask.post { cb(index * 100 / size) }
                    }
                }
            }
            file.path
        }.await(NetTask)
    }

    suspend fun doDownloadBin(id: Int): ByteArray {
        val url = "/dev_file/get?id=${id}"
        return PromiseTask{ doGet(url) }.await(NetTask)
    }

    @kotlinx.serialization.Serializable
    data class StoreFile(
        val id: Int,
        val description: String,
        val version: String,
        val size: Int,
        val create_timestamp: Long
    ) {

        fun time(): String {
            val calendar = Calendar.getInstance()
            calendar.time = Date(create_timestamp * 1000)
            val year = calendar.get(Calendar.YEAR)
            val month = calendar.get(Calendar.MONTH) + 1
            val day = calendar.get(Calendar.DAY_OF_MONTH)
            val hour = calendar.get(Calendar.HOUR_OF_DAY)
            val minute = calendar.get(Calendar.MINUTE)
            val sec = calendar.get(Calendar.SECOND)
            return "$year-$month-$day $hour:$minute:$sec"
        }
    }

    suspend fun queryMCUFile(): Array<StoreFile> {
        val body = PromiseTask {
            doGet("/dev_file/query?type_id=1")
        }.await(NetTask)
        return defJson.decodeFromString( String(body) )
    }

    suspend fun queryApkFile(): Array<StoreFile> {
        val body = PromiseTask {
            doGet("/dev_file/query?type_id=2")
        }.await(NetTask)
        return defJson.decodeFromString( String(body) )
    }
}

